package xidian.vlab.service.impl;

import org.openstack4j.api.Builders;
import org.openstack4j.model.common.Identifier;
import org.openstack4j.model.compute.Server;
import org.openstack4j.model.compute.ServerCreate;
import org.openstack4j.model.image.v2.Image;
import org.openstack4j.model.network.IPVersionType;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.openstack.OSFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xidian.vlab.config.Properties;
import xidian.vlab.domin.Log;
import xidian.vlab.domin.Room;
import xidian.vlab.domin.User.Student;
import xidian.vlab.domin.User.Teacher;
import xidian.vlab.domin.VMachine;
import xidian.vlab.dto.*;
import xidian.vlab.repository.RoomRepository;
import xidian.vlab.repository.User.StudentRepository;
import xidian.vlab.repository.User.TeacherRepository;
import xidian.vlab.repository.VMachineRepository;
import xidian.vlab.service.LogService;
import xidian.vlab.service.RoomService;
import xidian.vlab.utils.CsvUtil;
import xidian.vlab.utils.ResUtil;
import org.openstack4j.api.OSClient.OSClientV3;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;


@Service("RoomService")
public class RoomServiceImpl implements RoomService {
    @Autowired
    private RoomRepository roomRepo;
    @Autowired
    private StudentRepository stuRepo;
    @Autowired
    private TeacherRepository tchRepo;
    @Autowired
    private VMachineRepository machineRepo;
    @Autowired
    private LogService logService;
    @Autowired
    private Properties properties;

    @Override
    public ResJsonDTO getList(QueryDTO query){
        ListDTO list = new ListDTO();
        List<RoomDTO> roomList = new ArrayList<>();
        if (query.getAid() != null) {
            List<Room> rooms = roomRepo.findAll();
            for (Room room : rooms) {
                roomList.add(new RoomDTO(
                        room.getRid(),
                        room.getCid(),
                        room.getName(),
                        room.getStatus(),
                        room.getProfile(),
                        room.getMachines().size(),
                        new Date()
                ));
            }
            list.setRoomList(roomList);
            list.setTotal(rooms.size());
            logService.addLog(new Log(
                    query.getAid(),
                    "管理员",
                    "获取教室列表",
                    "成功",
                    null,
                    new Date()
            ));
            System.out.println("管理员["+query.getAid()+"]获取教室列表成功");
            return ResUtil.OK(list);
        }
        if (query.getTid() != null) {
            Teacher teacher = tchRepo.findById(query.getTid()).orElse(null);
            if (teacher == null) {
                logService.addLog(new Log(
                        query.getTid(),
                        "教师",
                        "获取教室列表",
                        "失败",
                        "未查找的该ID",
                        new Date()
                ));
                return ResUtil.ERROR("Can't find teacher "+query.getTid());
            }
            if (query.getStatus() == null) {
                for (Room room : teacher.getRooms()) {
                    roomList.add(new RoomDTO(
                            room.getRid(),
                            room.getCid(),
                            room.getName(),
                            room.getStatus(),
                            room.getProfile(),
                            room.getMachines().size(),
                            new Date()
                    ));
                }
            }
            else {
                for (Room room : teacher.getRooms()) {
                    if (room.getStatus().equals(query.getStatus())){
                        roomList.add(new RoomDTO(
                                room.getRid(),
                                room.getCid(),
                                room.getName(),
                                room.getStatus(),
                                room.getProfile(),
                                room.getMachines().size(),
                                new Date()
                        ));
                    }
                }
            }
            list.setRoomList(roomList);
            list.setTotal(teacher.getRooms().size());//fix here 合并使用一个list.set?
            logService.addLog(new Log(
                    query.getTid(),
                    teacher.getName(),
                    "获取教室列表",
                    "成功",
                    null,
                    new Date()
            ));
            System.out.println("教师["+query.getTid()+"]获取教室列表成功");
            return ResUtil.OK(list);
        }
        logService.addLog(new Log(
                null,
                "未知",
                "获取教室列表",
                "失败",
                "query错误",
                new Date()
        ));
        System.out.println("Room Request: query 错误;"+query);
        return ResUtil.ERROR("query错误");
    }

    @Override
    public ResJsonDTO createRoom(FormDTO form){
        Date date = new Date();
        if (form.getTid() == null) {
            logService.addLog(new Log(
                    form.getTid(),
                    "教师",
                    "提交教室申请",
                    "失败",
                    "未输入教师ID",
                    new Date()
            ));
            return ResUtil.ERROR("教师ID错误!");
        }
        if (form.getFile().isEmpty()) {
            logService.addLog(new Log(
                    form.getTid(),
                    "教师",
                    "提交教室申请",
                    "失败",
                    "CSV文件错误",
                    new Date()
            ));
            return ResUtil.ERROR("csv文件为空!");
        }
        if (!Objects.requireNonNull(form.getFile().getOriginalFilename()).endsWith(".csv")){
            logService.addLog(new Log(
                    form.getTid(),
                    "教师",
                    "提交教室申请",
                    "失败",
                    "文件类型错误",
                    new Date()
            ));
            return ResUtil.ERROR("文件类型错误!");
        }

        final Path path = Paths.get(properties.getUploadDir(), "tmp.csv");

        try {
            Files.write(path, form.getFile().getBytes());
            if (!CsvUtil.isValid(path,"学号,姓名")) {
                return ResUtil.ERROR("CSV文件内容格式错误，请添加表头！");
            }
            List<String[]> list = CsvUtil.getList(path);
            if (list == null) {
                return ResUtil.ERROR("CSV文件读取错误");
            }
            if (list.size()>253) {//子网ip数量要大于学生数量，加判断
                return ResUtil.ERROR("学生数量超出限制!");
            }
            for (String[] row : list) {//判断所有名单中所有学生存在，移入csv isvalid中判断
               if (!stuRepo.existsById(Long.parseLong(row[0]))) {
                    return ResUtil.ERROR(row[0]+"学生用户不存在!");
                }
            }

            String ProjectName = Long.toString(form.getTid());
            Teacher teacher = tchRepo.findById(form.getTid()).orElse(null);
            if (teacher == null) {
                return ResUtil.ERROR("未找到该用户:"+ProjectName);
            }

            ///////////////////////////////////////////////
            //获取token
            OSClientV3 os = OSFactory.builderV3()
                    .endpoint(properties.getKeystoneAPI())
                    .credentials(ProjectName,teacher.getPassword(), Identifier.byName("Default"))
                    .scopeToProject(Identifier.byName(ProjectName), Identifier.byName("Default"))
                    .authenticate();

            ///////////////////////////////////////////////
            //创建教室对应的内网
            Network network = os.networking().network().create(Builders.network()
                    .name(form.getTitle())
                    .adminStateUp(true)
                    .tenantId(teacher.getProjectID())
                    .build());
            List<String> networks = new ArrayList<>();
            networks.add(network.getId());

            Room save = roomRepo.save(new Room(//创建教室
                    network.getId(),
                    form.getCid(),
                    form.getTitle(),
                    "锁定",
                    form.getProfile(),
                    date, date,
                    form.getEndTime(),
                    teacher
            ));

            //创建内网的子网
            Subnet subnet = os.networking().subnet().create(Builders.subnet()
                .name("Sub_"+save.getName())
                .networkId(network.getId())
                .tenantId(teacher.getProjectID())
                .addPool("192.168.0.2", "192.168.0.254")
                .ipVersion(IPVersionType.V4)
                .enableDHCP(true)
                .cidr("192.168.0.0/24")
                .build());
            ///////////////////////////////////////////////
            //根据名单创建虚拟机
            Student student = new Student();
            for (String[] row : list) {
                student.setSid(Long.parseLong(row[0]));
                ///////////////////////////////////////////////
                //创建虚拟机
                ServerCreate sc = Builders.server()
                        .name(row[0])
                        .flavor("1")
                        .image(form.getImgId())
                        .addSecurityGroup("default")
                        .networks(networks)
                        .build();

                Image image = os.imagesV2().get(form.getImgId());// fixme: 前端表单中填入imgName
                Server server = os.compute().servers().boot(sc); // fixme: 创建虚拟机失败时删除已创建的网络
//                Server server = os.compute().servers().bootAndWaitActive(sc, 10000);
//                System.out.println(server.getAddresses().getAddresses(network.getName()).get(0).getAddr());
                ///////////////////////////////////////////////
                machineRepo.save(new VMachine(
                        server.getId(),
                        null,
                        "cirros",
                        "gocubsgo",
                        "BUILD",
                        "暂无介绍",
                        image.getId(),
                        image.getName(),
                        date, date,
                        form.getEndTime(),
                        student,
                        save
                ));
            }
            ///////////////////////////////////////////////

            Files.delete(path);
            return ResUtil.OK();
        }catch (IOException e) {
            e.printStackTrace();
//            for (VMachine machine : save.getMachines()) {
//                os.compute().servers().delete(machine.getMid());
//            }
//            Thread.sleep(1000);//等待虚拟机删除完成
//            os.networking().network().delete(room.getRid());//虚拟机删除完之后，网络才能删除
            return ResUtil.ERROR("服务器内部错误!");
        }

    }
    @Override
    public ResJsonDTO removeRoom(QueryDTO query){
        if (query.getRid() == null) {
            return ResUtil.ERROR("remove room query error");
        }
        Room room = roomRepo.findById(query.getRid()).orElse(null);
        if (room == null) {
            return ResUtil.ERROR("Can't find room "+query.getRid());
        }
        //获取token
        OSClientV3 os = OSFactory.builderV3()
                .endpoint(properties.getKeystoneAPI())
                .credentials(room.getTeacher().getUserID(),room.getTeacher().getPassword())
                .scopeToProject(Identifier.byId(room.getTeacher().getProjectID()))
                .authenticate();
        //删除教室
        try {
            //删除教室内的虚拟机
            for (VMachine machine : room.getMachines()) {
                os.compute().servers().delete(machine.getMid());
            }
            Thread.sleep(1000);//等待虚拟机删除完成
            os.networking().network().delete(room.getRid());//虚拟机删除完之后，网络才能删除
        }catch (Exception e) {
            e.printStackTrace();
            return ResUtil.ERROR("服务器内部错误，请联系管理员进行处理!");
        }

        machineRepo.deleteAll(room.getMachines());
        roomRepo.delete(room);

        return ResUtil.OK();
    }

    @Override
    public ResJsonDTO updateRoom(QueryDTO query){
        if (query.getRid() == null) {
            logService.addLog(new Log(
                    query.getTid(),
                    "教师",
                    "更新教室状态",
                    "失败",
                    "query错误",
                    new Date()
            ));
            return ResUtil.ERROR("Update room query error");
        }
        Room room = roomRepo.findById(query.getRid()).orElse(null);
        if (room == null) {
            logService.addLog(new Log(
                    query.getTid(),
                    "教师",
                    "更新教室状态",
                    "失败",
                    "未查找到该教室",
                    new Date()
            ));
            return ResUtil.ERROR("Can't find room "+query.getRid());
        }
        if (query.getStatus() != null) room.setStatus(query.getStatus());
//        if (query.getProfile() != null)

        Room save = roomRepo.save(room);
        logService.addLog(new Log(
                query.getTid(),
                "教师",
                "更新教室状态",
                "成功",
                null,
                new Date()
        ));
        System.out.println("更新教室成功:"+ save);
        return ResUtil.OK();
    }
}
